package com.fh.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fh.apientity.ApiException;
import com.fh.apientity.ApiResponse;
import com.fh.apientity.Kline;
import com.fh.apientity.KlineResponse;
import com.fh.apientity.Merged;
import com.fh.apientity.MergedResponse;
import com.fh.apientity.Symbol;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.OkHttpClient.Builder;

public class ApiClientUtil {

	static final int CONN_TIMEOUT = 5;
    static final int READ_TIMEOUT = 5;
    static final int WRITE_TIMEOUT = 5;
    static final String API_HOST = "api.huobi.br.com";
    static final MediaType JSON = MediaType.parse("application/json");
    static final String API_URL = "https://" + API_HOST;
    static final OkHttpClient client = createOkHttpClient();
    static   String accessKeyId;
    static   String accessKeySecret;
    static  String assetPassword;
    
    
    /**
     * 创建一个ApiClient实例
         *
         * @param accessKeyId AccessKeyId
         * @param accessKeySecret AccessKeySecret
         * @param assetPassword AssetPassword
         */
        public ApiClientUtil(String accessKeyId, String accessKeySecret, String assetPassword) {
            this.accessKeyId = accessKeyId;
            this.accessKeySecret = accessKeySecret;
            this.assetPassword = assetPassword;
        }
        
	  /**
	 * 查询交易对
	     *
	     * @return List of symbols.
	     */
	    public static List<Symbol> getSymbols() {
	        ApiResponse<List<Symbol>> resp =get("/v1/common/symbols",null,  new TypeReference<ApiResponse<List<Symbol>>>() {
	                });
	        return resp.checkAndReturn();
	    }
	    
	    /**
	     * GET /market/history/kline 获取K线数据
	         *
	         * @param symbol
	         * @param period
	         * @param size
	         * @return
	         */
	        public static KlineResponse kline(String symbol, String period, String size) {
	            HashMap map = new HashMap();
	    		 map.put("symbol", symbol);
	    		 map.put("period", period);
	    		 map.put("size", size);
	            KlineResponse resp = get("/market/history/kline", map, new TypeReference<KlineResponse<List<Kline>>>() {
	            });
	            return resp;
	        }
	        
	        /**
		     * GET /market/history/kline 获取聚合行情
		         *
		         * @param symbol
		         * @param period
		         * @param size
		         * @return
		         */
        public static String merged(String symbol) {
            HashMap map = new HashMap();
    		 map.put("symbol", symbol);
    		 String resp = getMerged("/market/detail/merged", map) ;
            
            return resp;
        }
	    
	    static <T> T  get(String uri, Map<String, String> params, TypeReference<T> ref) {
		        if (params == null) {
		 params = new HashMap<>();
		        }
		        return call("GET", uri, null, params, ref);
	    }
	    
	    static String  getMerged(String uri, Map<String, String> params) {
	        if (params == null) {
	 params = new HashMap<>();
	        }
	        return callMerged("GET", uri, null, params);
    }
	    
	    static  <T> T call(String method, String uri, Object object, Map<String, String> params,
	               TypeReference<T> ref) {
		        ApiSignature sign = new ApiSignature();
//		 sign.createSignature(accessKeyId, accessKeySecret, method, API_HOST, uri, params);
		        try {
		            Request.Builder builder = null;
		            if ("POST".equals(method)) {
		                RequestBody body = RequestBody.create(JSON, JsonUtil.writeValue(object));
		 builder = new Request.Builder().url(API_URL + uri + "?" + toQueryString(params)).post(body);
		            } else {
		 builder = new Request.Builder().url(API_URL + uri + "?" + toQueryString(params)).get();
		            }
		            if (assetPassword !=null ) {
		 builder.addHeader("AuthData", authData());
		            }
		            Request request = builder.build();
		            Response response = client.newCall(request).execute();
		            String s = response.body().string();
		            return JsonUtil.readValue(s, ref);
		        } catch (IOException e) {
		            throw new ApiException(e);
		        }
	    }
	    
	    
	    static  String callMerged(String method, String uri, Object object, Map<String, String> params
	               ) {
		        ApiSignature sign = new ApiSignature();
//		 sign.createSignature(accessKeyId, accessKeySecret, method, API_HOST, uri, params);
		        try {
		            Request.Builder builder = null;
		            if ("POST".equals(method)) {
		                RequestBody body = RequestBody.create(JSON, JsonUtil.writeValue(object));
		 builder = new Request.Builder().url(API_URL + uri + "?" + toQueryString(params)).post(body);
		            } else {
		 builder = new Request.Builder().url(API_URL + uri + "?" + toQueryString(params)).get();
		            }
		            if (assetPassword !=null ) {
		 builder.addHeader("AuthData", authData());
		            }
		            Request request = builder.build();
		            Response response = client.newCall(request).execute();
		            String jsonMerged = response.body().string();
		            return  jsonMerged;
		        } catch (IOException e) {
		            throw new ApiException(e);
		        }
	    }
	    
	    static  String toQueryString(Map<String, String> params) {
	        return String.join("&", params.entrySet().stream().map((entry) -> {
	            return entry.getKey() + "=" + ApiSignature.urlEncode(entry.getValue());
	        }).collect(Collectors.toList()));
	    }
	    
	    static String authData() {
		        MessageDigest md = null;
		        try {
		 md = MessageDigest.getInstance("MD5");
		        } catch (NoSuchAlgorithmException e) {
		            throw new RuntimeException(e);
		        }
		 md.update(assetPassword.getBytes(StandardCharsets.UTF_8));
		 md.update("hello, moto".getBytes(StandardCharsets.UTF_8));
		        Map<String, String> map = new HashMap<>();
		 map.put("assetPwd", DatatypeConverter.printHexBinary(md.digest()).toLowerCase());
		        try {
		            return ApiSignature.urlEncode(JsonUtil.writeValue(map));
		        } catch (IOException e) {
		            throw new RuntimeException("Get json failed: " + e.getMessage());
		        }
	    }
	    
	    static OkHttpClient createOkHttpClient() {
		        return new Builder().connectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS)
		 .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS).writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
		 .build();
	    }
	    
	    
	   
}
	       
	    /**
	     * API签名，签名规范：
	     * <p>
	     * http://docs.aws.amazon.com/zh_cn/general/latest/gr/signature-version-2.html
	     *
	     * @Date 2018/1/14
	     * @Time 16:02
	     */
	    class ApiSignature {

//	        final Logger log = LoggerFactory.getLogger(getClass());

	        static final DateTimeFormatter DT_FORMAT = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss");
	        static final ZoneId ZONE_GMT = ZoneId.of("Z");
	       
	        /**
	     * 创建一个有效的签名。该方法为客户端调用，将在传入的params中添加AccessKeyId、Timestamp、SignatureVersion、SignatureMethod、Signature参数。
	         *
	         * @param appKey AppKeyId.
	         * @param appSecretKey AppKeySecret.
	         * @param method 请求方法，"GET"或"POST"
	         * @param host 请求域名，例如"be.huobi.com"
	         * @param uri 请求路径，注意不含?以及后的参数，例如"/v1/api/info"
	         * @param params 原始请求参数，以Key-Value存储，注意Value不要编码
	         */
	        public void createSignature(String appKey, String appSecretKey, String method, String host,
	                                    String uri, Map<String, String> params) {
	            StringBuilder sb = new StringBuilder(1024);
	    		 sb.append(method.toUpperCase()).append('\n') // GET
	    		 .append(host.toLowerCase()).append('\n') // Host
	    		 .append(uri).append('\n'); // /path
	    		 params.remove("Signature");
	    		 params.put("AccessKeyId", appKey);
	    		 params.put("SignatureVersion", "2");
	    		 params.put("SignatureMethod", "HmacSHA256");
	    		 params.put("Timestamp", gmtNow());
	    		        // build signature:
	    		        SortedMap<String, String> map = new TreeMap<>(params);
	    		        for (Map.Entry<String, String> entry : map.entrySet()) {
	    		            String key = entry.getKey();
	    		            String value = entry.getValue();
	    		 sb.append(key).append('=').append(urlEncode(value)).append('&');
	    		        }
	    		        // remove last '&':
	    		 sb.deleteCharAt(sb.length() - 1);
	    		        // sign:
	    		        Mac hmacSha256 =null ;
	    		        try {
	    		 hmacSha256 = Mac.getInstance("HmacSHA256");
	    		            SecretKeySpec secKey =
	    		                    new SecretKeySpec(appSecretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
	    		 hmacSha256.init(secKey);
	    		        } catch (NoSuchAlgorithmException e) {
	    		            throw new RuntimeException("No such algorithm: " + e.getMessage());
	    		        } catch (InvalidKeyException e) {
	    		            throw new RuntimeException("Invalid key: " + e.getMessage());
	    		        }
	    		        String payload = sb.toString();
	    		        byte[] hash = hmacSha256.doFinal(payload.getBytes(StandardCharsets.UTF_8));
	    		        String actualSign = Base64.getEncoder().encodeToString(hash);
	    		    params.put("Signature", actualSign);
	    		        
	    		
	        }
	        

	        /**
	     * 使用标准URL Encode编码。注意和JDK默认的不同，空格被编码为%20而不是+。
	         *
	         * @param s String字符串
	         * @return URL编码后的字符串
	         */
	        public static String urlEncode(String s) {
	            try {
	                return URLEncoder.encode(s, "UTF-8").replaceAll("\\+", "%20");
	            } catch (UnsupportedEncodingException e) {
	                throw new IllegalArgumentException("UTF-8 encoding not supported!");
	            }
	        }

	        /**
	     * Return epoch seconds
	         */
	        long epochNow() {
	            return Instant.now().getEpochSecond();
	        }

	        String gmtNow() {
	            return Instant.ofEpochSecond(epochNow()).atZone(ZONE_GMT).format(DT_FORMAT);
	        }
	    }
	    
	    class JsonUtil {

            public static String writeValue(Object obj) throws IOException {
                return objectMapper.writeValueAsString(obj);
            }

            public static <T> T readValue(String s, TypeReference<T> ref) throws IOException {
                return objectMapper.readValue(s, ref);
            }

            static final ObjectMapper objectMapper = createObjectMapper();                                                                                                        

            static ObjectMapper createObjectMapper() {
                final ObjectMapper mapper = new ObjectMapper();
//         mapper.setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
         mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
                // disabled features:
         mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
         mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
         mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                return mapper;
            }
            
        }
        
